/*	
Copyright (c) 2007 Dancing Tortoise Software
 
	Permission is hereby granted, free of charge, to any person 
	obtaining a copy of this software and associated documentation
	files (the "Software"), to deal in the Software without 
	restriction, including without limitation the rights to use, 
	copy, modify, merge, publish, distribute, sublicense, and/or 
	sell copies of the Software, and to permit persons to whom the
	Software is furnished to do so, subject to the following 
	conditions:
 
	The above copyright notice and this permission notice shall be
	included in all copies or substantial portions of the Software.
 
	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
	OTHER DEALINGS IN THE SOFTWARE.
 
    TSSTSessionWindowController.m
 */


#import <Carbon/Carbon.h>
#import "NFIWindow.h"
#import "SimpleComicAppDelegate.h"
#import "TSSTSessionWindowController.h"
#import "TSSTKeyWindow.h"
#import "TSSTPageView.h"
#import "TSSTSortDescriptor.h"
#import "TSSTImageUtilities.h"
#import "TSSTPage.h"
#import "TSSTSegmentedControl.h"
#import "TSSTCRTProgressBar.h"
#import "TSSTInfoWindow.h"
#import "TSSTThumbnailView.h"


@implementation TSSTSessionWindowController


/*  Inits the window controller and sets the session.
    If this is a new session as opposed to a saved session
    then the window cascades */
- (id)initWithSession:(NSManagedObject *)aSession
{
    self = [super init];
    if (self != nil)
    {
		closing = NO;
        loupe = NO;
        bezelAnimation = nil;
        session = [aSession retain];
        BOOL cascade = [session valueForKey: @"position"] ? NO : YES;
        [self setShouldCascadeWindows: cascade];
    }
    return self;
}



- (NSString *)windowNibName
{
    return @"TSSTSessionWindow";
}


/*  Sets up all of the observers and bindings. */
- (void)awakeFromNib
{
    /* This needs to be set as the window subclass that the expose window
        uses has mouse events turned off by default */
    [exposeBezel setIgnoresMouseEvents: NO];
    [exposeBezel setFloatingPanel: YES];
    [[self window] setAcceptsMouseMovedEvents: YES];
    /*  This needs to be set so that mouse moved events from the fullscreen window
        are passed to its delegate, this window controller */
    [fullscreenWindow setNextResponder: self];
    [fullscreenWindow setAcceptsMouseMovedEvents: YES];
    
    /*  Called to setup the tooltips on the custom segmented controlls. */
    [self setupSegmentedControls];
    [(NFIWindow *)[self window] setBottomBarHeight: 28];
    
    [pageController setSelectionIndex: [[session valueForKey: @"selection"] intValue]];

    NSUserDefaultsController * defaults = [NSUserDefaultsController sharedUserDefaultsController];
    
    [defaults addObserver: self forKeyPath: @"values.constrainScale" options: 0 context: nil];
    [defaults addObserver: self forKeyPath: @"values.scrollersVisible" options: 0 context: nil];
    [defaults addObserver: self forKeyPath: @"values.pageBackgroundColor" options: 0 context: nil];
    [session addObserver: self forKeyPath: TSSTFullscreen options: 0 context: nil];
    [session addObserver: self forKeyPath: TSSTPageOrder options: 0 context: nil];
    [session addObserver: self forKeyPath: TSSTPageScaleOptions options: 0 context: nil];
    [session addObserver: self forKeyPath: TSSTTwoPageSpread options: 0 context: nil];
    [session bind: @"selection" toObject: pageController withKeyPath: @"selectionIndex" options: nil];

    [pageController addObserver: self forKeyPath: @"selectionIndex" options: 0 context: nil];
    [pageController addObserver: self forKeyPath: @"arrangedObjects.@count" options: 0 context: nil];
    
    [progressBar addObserver: self forKeyPath: @"currentValue" options: 0 context: nil];
    [progressBar bind: @"currentValue" toObject: pageController withKeyPath: @"selectionIndex" options: nil];
    [progressBar bind: @"maxValue" toObject: pageController withKeyPath: @"arrangedObjects.@count" options: nil];
    [progressBar bind: @"leftToRight" toObject: session withKeyPath: TSSTPageOrder options: nil];
    
    [pageView bind: TSSTViewRotation toObject: session withKeyPath: TSSTViewRotation options: nil];
    [pageView bind: TSSTZoomLevel toObject: session withKeyPath: TSSTZoomLevel options: nil];
    
    [self restoreSession];
}



- (void)dealloc
{
    [(TSSTThumbnailView *)exposeView setDataSource: nil];

    [bezelAnimation stopAnimation];    
    NSUserDefaultsController * defaults = [NSUserDefaultsController sharedUserDefaultsController];

    [defaults removeObserver: self forKeyPath: @"values.scrollersVisible"];
	[defaults removeObserver: self forKeyPath: @"values.pageBackgroundColor"];
    [defaults removeObserver: self forKeyPath: @"values.constrainScale"];
    [pageController removeObserver: self forKeyPath: @"selectionIndex"];
    [pageController removeObserver: self forKeyPath: @"arrangedObjects.@count"];
    [[NSNotificationCenter defaultCenter] removeObserver: self];
    
    [session removeObserver: self forKeyPath: TSSTFullscreen];
    [session removeObserver: self forKeyPath: TSSTPageOrder];
    [session removeObserver: self forKeyPath: TSSTPageScaleOptions];
    [session removeObserver: self forKeyPath: TSSTTwoPageSpread];
    [session unbind: TSSTZoomLevel];
    [session unbind: TSSTViewRotation];
    [session unbind: @"selection"];
    
    [progressBar removeObserver: self forKeyPath: @"currentValue"];
    [progressBar unbind: @"currentValue"];
    [progressBar unbind: @"maxValue"];
    [progressBar unbind: @"leftToRight"];
        
    [pageView setDataSource: nil];

    [session release];
    [pageNames release];
    [super dealloc];
}



/*  Observes changes to the page controller.  Changes are reflected by the 
    page view.  */
- (void)observeValueForKeyPath:(NSString *)keyPath
					  ofObject:(id)object 
						change:(NSDictionary *)change 
					   context:(void *)context
{
    if(closing)
	{
		return;
	}
	
	
    if([[pageController arrangedObjects] count] <= 0)
    {
        SetSystemUIMode(kUIModeNormal, 0);
        [[NSApp delegate] endSession: self];
        return;
    }

    if([keyPath isEqualToString: TSSTFullscreen])
    {
        [self fullscreen];
    }
    else if([keyPath isEqualToString: TSSTPageScaleOptions] || 
            [keyPath isEqualToString: @"values.scrollersVisible"])
    {
        [self scaleToWindow];
    }
    else if([keyPath isEqualToString: @"currentValue"])
    {
        [pageController setSelectionIndex: [progressBar currentValue]];
    }
    else if([keyPath isEqualToString: @"arrangedObjects.@count"])
    {
        [NSThread detachNewThreadSelector: @selector(processThumbs) toTarget: exposeView withObject: nil];
        [self changeViewImages];
    }
    else if([keyPath isEqualToString: TSSTPageOrder])
	{
		[(TSSTThumbnailView *)exposeView setNeedsDisplay: YES];
		[(TSSTThumbnailView *)exposeView buildTrackingRects];
        [self changeViewImages];
	}
	else if([keyPath isEqualToString: @"values.pageBackgroundColor"])
	{
		NSDictionary * defaultValues = [[NSUserDefaultsController sharedUserDefaultsController] values];
		NSColor * color = [NSUnarchiver unarchiveObjectWithData: [defaultValues valueForKey: TSSTBackgroundColor]];
		[pageScrollView setBackgroundColor: color];
	}
	else 
	{
        [self changeViewImages];
    }
}



#pragma mark -
#pragma mark Progress Bar



- (NSImage *)imageForPageAtIndex:(int)index
{
    return [[[pageController arrangedObjects] objectAtIndex: index] valueForKey: @"thumbnail"];
}



- (NSString *)nameForPageAtIndex:(int)index
{
    
    return [[[pageController arrangedObjects] objectAtIndex: index] valueForKey: @"name"];
}



- (NSString *)groupNameForPageAtIndex:(int)index
{
    return [[[[pageController arrangedObjects] objectAtIndex: index] valueForKeyPath: @"group.path"] lastPathComponent];
}


- (int)pageCount
{
    return [[pageController content] count];
}


#pragma mark -
#pragma mark Event handling



- (void)mouseMoved:(NSEvent *)theEvent
{
    NSPoint location = [theEvent locationInWindow];
    NSRect progressRect = [progressBar convertRect: [progressBar progressRect] toView: nil];
    if(NSMouseInRect(location, progressRect, [progressBar isFlipped]))
    {
        [self infoPanelSetupAtPoint: location];
    }

    [self refreshLoupePanel];
}



- (void)mouseEntered:(NSEvent *)theEvent
{
    if([theEvent trackingNumber] == progressRectTrackTag)
    {
        [self infoPanelSetupAtPoint: [theEvent locationInWindow]];
        [[self window] addChildWindow: infoWindow ordered: NSWindowAbove];
    }
}



- (void)mouseExited:(NSEvent *)theEvent
{
    if([theEvent trackingNumber] == progressRectTrackTag)
    {
        [[infoWindow parentWindow] removeChildWindow: infoWindow];
        [infoWindow orderOut: self];
    }
}



- (void)refreshLoupePanel
{
    NSPoint mouse = [NSEvent mouseLocation];
    NSWindow * currentWindow = [[session valueForKey: TSSTFullscreen] boolValue] ? fullscreenWindow : [self window];
    NSPoint localPoint = [pageView convertPoint: [currentWindow convertScreenToBase: mouse] fromView: nil];
	NSPoint scrollPoint = [pageScrollView convertPoint: [currentWindow convertScreenToBase: mouse] fromView: nil];
    if(!NSMouseInRect(scrollPoint, [pageScrollView bounds], [pageScrollView isFlipped]) || !loupe)
    {
        if([loupeWindow isVisible])
        {
            [[loupeWindow parentWindow] removeChildWindow: loupeWindow];
            [loupeWindow orderOut: self];
        }
        
        [NSCursor unhide];
        return;
    }
    
    if(![loupeWindow isVisible] && loupe)
    {
        [currentWindow addChildWindow: loupeWindow ordered: NSWindowAbove];
        [NSCursor hide];
    }
    
    if(loupe)
    {
        NSRect zoomRect = [zoomView frame];
        [loupeWindow centerAtPoint: mouse];
        zoomRect.origin = localPoint;
        [zoomView setImage: [pageView imageInRect: zoomRect]];
    }
}



- (void)infoPanelSetupAtPoint:(NSPoint)point
{
    NSPoint cursorPoint = [progressBar convertPoint: point fromView: nil];
    point.y = 20;
    int index = [progressBar indexForPoint: cursorPoint];
    [pageName setStringValue: [self nameForPageAtIndex: index]];
    [groupName setStringValue: [self groupNameForPageAtIndex: index]];
    float textFieldWidth = [[pageName attributedStringValue] size].width;
    float groupWidth = [[groupName attributedStringValue] size].width;
    textFieldWidth = textFieldWidth > groupWidth ? textFieldWidth : groupWidth;
    textFieldWidth += 5;
    
    NSImage * thumb = [self imageForPageAtIndex: index];
    NSSize imageViewSize = [infoPicture frame].size;
    NSSize thumbSize = [thumb size];
    imageViewSize.width = (thumbSize.width >= thumbSize.height) ? 128 : 128 / thumbSize.height * thumbSize.width;
    [infoPicture setFrameSize: imageViewSize];
    [infoPicture setImage: thumb];
    
    NSRect textFieldRect = [pageName frame];
    textFieldRect.size.width = textFieldWidth;
    textFieldRect.origin.x = imageViewSize.width + 10;
    [pageName setFrame: textFieldRect];
    
    textFieldRect.origin.y = [groupName frame].origin.y;
    [groupName setFrame: textFieldRect];

    NSRect infoRect = [infoWindow frame];
    infoRect.size.width = imageViewSize.width + textFieldRect.size.width + 15;
    cursorPoint = [[self window] convertBaseToScreen: point];
    [infoWindow caretAtPoint: cursorPoint size: infoRect.size withLimitLeft: NSMinX([[progressBar window] frame]) right: NSMaxX([[progressBar window] frame])];
}



#pragma mark -
#pragma mark Actions



- (IBAction)removePages:(id)sender
{
    NSManagedObject * page = [[pageController selectedObjects] objectAtIndex: 0];
    [pageController removeObject: page];
    [[self managedObjectContext] deleteObject: page];
}



- (IBAction)changeTwoPage:(id)sender
{
    BOOL spread = [[session valueForKey: TSSTTwoPageSpread] boolValue];
    [session setValue: [NSNumber numberWithBool: !spread] forKey: TSSTTwoPageSpread];
}



- (IBAction)changePageOrder:(id)sender
{
    BOOL pageOrder = [[session valueForKey: TSSTPageOrder] boolValue];
    [session setValue: [NSNumber numberWithBool: !pageOrder] forKey: TSSTPageOrder];
}



- (IBAction)changeFullscreen:(id)sender
{
    BOOL fullscreen = [[session valueForKey: TSSTFullscreen] boolValue];
    [session setValue: [NSNumber numberWithBool: !fullscreen] forKey: TSSTFullscreen];
}



- (IBAction)changeScaling:(id)sender
{
    int scaleType = [sender tag] % 400;
    [session setValue: [NSNumber numberWithInt: scaleType] forKey: TSSTPageScaleOptions];
}



/*! Method flips the page to the right calling nextPage or previousPage
depending on the prefered page ordering.
*/
- (IBAction)pageRight:(id)sender
{
    [self setPageTurn: 2];
    if([[session valueForKey: TSSTPageOrder] boolValue])
    {
        [self nextPage];
    }
    else
    {
        [self previousPage];
    }
}



/*! Method flips the page to the left calling nextPage or previousPage
    depending on the prefered page ordering.
*/
- (IBAction)pageLeft:(id)sender
{
    [self setPageTurn: 1];
    if([[session valueForKey: TSSTPageOrder] boolValue])
    {
        [self previousPage];
    }
    else
    {
        [self nextPage];
    }
}



- (IBAction)shiftPageRight:(id)sender
{
    if([[session valueForKey: TSSTPageOrder] boolValue])
    {
        [pageController selectNext: sender];
    }
    else
    {
        [pageController selectPrevious: sender];
    }
}



- (IBAction)shiftPageLeft:(id)sender
{
    if([[session valueForKey: TSSTPageOrder] boolValue])
    {
        [pageController selectPrevious: sender];
    }
    else
    {
        [pageController selectNext: sender];
    }
}



- (IBAction)skipRight:(id)sender
{
    int index;
    if([[session valueForKey: TSSTPageOrder] boolValue])
    {
        index = ([pageController selectionIndex] + 10);
        index = index < [[pageController content] count] ? index : [[pageController content] count] - 1;
    }
    else
    {
        index = ([pageController selectionIndex] - 10);
        index = index > 0 ? index : 0;
    }
    
    [pageController setSelectionIndex: index];
}



- (IBAction)skipLeft:(id)sender
{
    int index;
    if(![[session valueForKey: TSSTPageOrder] boolValue])
    {
        index = ([pageController selectionIndex] + 10);
        index = index < [[pageController content] count] ? index : [[pageController content] count] - 1;
    }
    else
    {
        index = ([pageController selectionIndex] - 10);
        index = index > 0 ? index : 0;
    }
    [pageController setSelectionIndex: index];
}



- (IBAction)firstPage:(id)sender
{
    [pageController setSelectionIndex: 0];
}



- (IBAction)lastPage:(id)sender
{
    [pageController setSelectionIndex: [[pageController content] count] - 1];
}



- (IBAction)zoomIn:(id)sender
{
    int scalingOption = [[session valueForKey: TSSTPageScaleOptions] intValue];
    int previousZoom = [[session valueForKey: TSSTZoomLevel] intValue];
    if(scalingOption != 0)
    {
        float factor = NSWidth([pageView imageBounds]) / [pageView combinedImageSizeForZoomLevel: 0].width;
        previousZoom = (factor * 10) - 10;
        [session setValue: [NSNumber numberWithInt: 0] forKey: TSSTPageScaleOptions];
    }
    
    [session setValue: [NSNumber numberWithInt: ++previousZoom] forKey: TSSTZoomLevel];
    [self refreshLoupePanel];
}



- (IBAction)zoomOut:(id)sender
{
    int scalingOption = [[session valueForKey: TSSTPageScaleOptions] intValue];
    int previousZoom = [[session valueForKey: TSSTZoomLevel] intValue];
    if(scalingOption != 0)
    {
        float factor = NSWidth([pageView imageBounds]) / [pageView combinedImageSizeForZoomLevel: 0].width;
        previousZoom = (factor * 10) - 10;
        [session setValue: [NSNumber numberWithInt: 0] forKey: TSSTPageScaleOptions];
    }
    
    [session setValue: [NSNumber numberWithInt: (previousZoom > -9 ? previousZoom - 1 : previousZoom)] forKey: TSSTZoomLevel];
    [self refreshLoupePanel];
}



- (IBAction)zoomReset:(id)sender
{
    [session setValue: [NSNumber numberWithInt: 0] forKey: TSSTZoomLevel];
    [self refreshLoupePanel];
}



- (IBAction)rotateRight:(id)sender
{
    int currentRotation = [[session valueForKey: TSSTViewRotation] intValue];
    currentRotation = currentRotation + 1 > 3 ? 0 : currentRotation + 1;
    [session setValue: [NSNumber numberWithInt: currentRotation] forKey: TSSTViewRotation];
    [self resizeWindow];
    [self refreshLoupePanel];
}



- (IBAction)rotateLeft:(id)sender
{
    int currentRotation = [[session valueForKey: TSSTViewRotation] intValue];
    currentRotation = currentRotation - 1 < 0 ? 3 : currentRotation - 1;
    [session setValue: [NSNumber numberWithInt: currentRotation] forKey: TSSTViewRotation];
    [self resizeWindow];
    [self refreshLoupePanel];
}



- (IBAction)toggleLoupe:(id)sender
{
    loupe = !loupe;
    [self refreshLoupePanel];
}



- (IBAction)togglePageExpose:(id)sender
{
    if([exposeBezel isVisible])
    {
        [[thumbnailPanel parentWindow] removeChildWindow: thumbnailPanel];
        [thumbnailPanel orderOut: self];
        [exposeBezel orderOut: self];
    }
    else
    {
        [(TSSTThumbnailView *)exposeView buildTrackingRects];
        [exposeBezel setFrame: [[[self window] screen] frame] display: NO];
        [exposeBezel makeKeyAndOrderFront: self];
        [exposeBezel addChildWindow: thumbnailPanel ordered: NSWindowAbove];
        [NSThread detachNewThreadSelector: @selector(processThumbs) toTarget: exposeView withObject: nil];
    }
}



- (IBAction)changePage:(id)sender
{
    int index = [pagelistMenu indexOfItem: sender];
    [pageController setSelectionIndex: index];
}



- (IBAction)launchJumpPanel:(id)sender
{
	[jumpField setIntValue: [pageController selectionIndex] + 1];
	NSWindow * current = [fullscreenWindow isVisible] ? fullscreenWindow : [self window];
	[NSApp beginSheet: jumpPanel modalForWindow: current modalDelegate: self didEndSelector: @selector(closeSheet:) contextInfo: NULL];
}



- (IBAction)cancleJumpPanel:(id)sender
{
	[NSApp endSheet: jumpPanel returnCode: 0];
}



- (IBAction)goToPage:(id)sender
{
    if([jumpField intValue] != NSNotFound)
    {
        int index = [jumpField intValue] < 1 ? 0 : [jumpField intValue] - 1;
        [pageController setSelectionIndex: index];
    }
	[NSApp endSheet: jumpPanel returnCode: 1];
}



- (void)closeSheet:(int)code
{
	[jumpPanel close];
}


#pragma mark -
#pragma mark Convenience Methods



/* This method sets the tool tips for all of the segmented controls in the status bar. */
- (void)setupSegmentedControls
{
    [[scalingControl cell] setToolTip: NSLocalizedString(@"Original Size", @"Pages are not scaled") forSegment: 0];
    [[scalingControl cell] setToolTip: NSLocalizedString(@"Fit to Window", @"Pages are scaled to fit the window") forSegment: 1];
    [[scalingControl cell] setToolTip: NSLocalizedString(@"Horizontal Fit", @"Pages are scaled to fit window width") forSegment: 2];
    [scalingControl setupTooptips];
    
    [[layoutControl cell] setToolTip: NSLocalizedString(@"Single Page Layout", @"Only a single page is displayed at a time") forSegment: 0];
    [[layoutControl cell] setToolTip: NSLocalizedString(@"Two Page Layout", @"Two pages are displayed side by side") forSegment: 1];
    [layoutControl setupTooptips];
    
    [[directionControl cell] setToolTip: NSLocalizedString(@"Right to Left Page Order", nil) forSegment: 0];
    [[directionControl cell] setToolTip: NSLocalizedString(@"Left to Right Page Order", nil)  forSegment: 1];
    [directionControl setupTooptips];
}



/*  When a session is launched this method is called.  It checks to see if the 
    session was a saved session or one that is brand new.  If it was a saved 
    session then all of the saved session information is passed to the window
    and view. */
- (void)restoreSession
{
    [self changeViewImages];
    [self scaleToWindow];
    NSDictionary * defaultValues = [[NSUserDefaultsController sharedUserDefaultsController] values];
    int loupeDiameter = [[defaultValues valueForKey: TSSTLoupeDiameter] intValue];
    [loupeWindow setFrame:NSMakeRect(0,0, loupeDiameter, loupeDiameter) display: NO];
    NSColor * color = [NSUnarchiver unarchiveObjectWithData: [defaultValues valueForKey: TSSTBackgroundColor]];
	[pageScrollView setBackgroundColor: color];
    [pageView setZoomLevel: [[session valueForKey: TSSTZoomLevel] intValue]];
    [pageView setRotation: [[session valueForKey: TSSTViewRotation] intValue]];
    [self rebuildPageMenu];
    NSValue * positionValue;
    NSData * posData = [session valueForKey: @"position"];
    if(posData)
    {
        positionValue = [NSUnarchiver unarchiveObjectWithData: posData];
        [[self window] setFrame: [positionValue rectValue] display: NO];
    }
	
    posData = [session valueForKey: TSSTScrollPosition];
    if(posData)
    {
		[self setShouldCascadeWindows: NO];
        positionValue = [NSUnarchiver unarchiveObjectWithData: posData];
        [pageView scrollPoint: [positionValue pointValue]];
    }
    else
    {
		[self setShouldCascadeWindows: YES];
        if(![[defaultValues valueForKey: TSSTWindowAutoResize] boolValue])
        {
            [[self window] zoom: self];
        }
        [pageView correctViewPoint];
    }
}



/*  This method figures out which pages should be displayed in the view.  
    To do so it looks at which page is currently selected as well as its aspect ratio
    and that of the next image */
- (void)changeViewImages
{
    int count = [[pageController arrangedObjects] count];
    int index = [pageController selectionIndex];
    TSSTPage * pageOne = [[pageController arrangedObjects] objectAtIndex: index];
    TSSTPage * pageTwo = (index + 1) < count ? [[pageController arrangedObjects] objectAtIndex: (index + 1)] : nil;
    NSString * titleString = [[pageOne valueForKey: @"name"] lastPathComponent];
    
    if([pageOne hasAllowedAspectRatio] && [pageTwo hasAllowedAspectRatio] && [[session valueForKey: TSSTTwoPageSpread] boolValue])
    {
        if([[session valueForKey: TSSTPageOrder] boolValue])
        {
            titleString = [NSString stringWithFormat:@"%@ %@", titleString, [[pageTwo valueForKey: @"name"] lastPathComponent]];
        }
        else
        {
            titleString = [NSString stringWithFormat:@"%@ %@", [[pageTwo valueForKey: @"name"] lastPathComponent], titleString];
        }
    }
    else
    {
        pageTwo = nil;
    }
	
	[[self window] setRepresentedFilename: [pageOne valueForKeyPath: @"group.path"]];
    [self setValue: titleString forKey: @"pageNames"];
    [pageView setFirstPage: [pageOne valueForKey: @"pageImage"] secondPageImage: [pageTwo valueForKey: @"pageImage"]];
    
    [self rebuildPageMenu];
    [self resizeWindow];

    [self refreshLoupePanel];
}



- (void)resizeWindow
{
    if(![[session valueForKey: TSSTFullscreen] boolValue] &&
       [[[[NSUserDefaultsController sharedUserDefaultsController] values] valueForKey: TSSTWindowAutoResize] boolValue])
    {
        NSRect allowedRect = [[[self window] screen] visibleFrame];
        NSRect zoomFrame = [self windowWillUseStandardFrame: [self window] defaultFrame: allowedRect];
        [[self window] setFrame: zoomFrame display: YES animate: NO];
    }
}



- (void)rebuildPageMenu
{
    NSEnumerator * pageEnumerator = [[pagelistMenu itemArray] objectEnumerator];
    NSMenuItem * pageItem;

    while (pageItem = [pageEnumerator nextObject])
    {
        [pagelistMenu removeItem: pageItem];
    }
    
    pageEnumerator = [[pageController arrangedObjects] objectEnumerator];
    NSString * menuItemName;
    while (menuItemName = [[pageEnumerator nextObject] valueForKey: @"name"])
    {
        pageItem = [[NSMenuItem alloc] initWithTitle: menuItemName action: @selector(changePage:) keyEquivalent: @""];
        [pagelistMenu addItem: pageItem];
        [pageItem release];
    }
    
    [[pagelistMenu itemAtIndex: [pageController selectionIndex]] setState: NSOnState];
}


- (void)fullscreen
{
    NSRect contentRect = NSZeroRect;
    [pageScrollView retain];
    
    if([[session valueForKey: TSSTFullscreen] boolValue])
    {
        NSValue * rectangleValue = [NSValue valueWithRect: [[self window] frame]];
        NSData * rectData = [NSArchiver archivedDataWithRootObject: rectangleValue];
        [session setValue: rectData forKey: @"position" ];
        
        SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);
        [[self window] orderOut: self];
        [fullscreenWindow setFrame: [[[self window] screen] frame] display: NO];
        [pageScrollView removeFromSuperview];
        [[fullscreenWindow contentView] addSubview: pageScrollView];
        contentRect.size = [[pageScrollView window] frame].size;
        [pageScrollView setFrame: contentRect];
        [pageView resizeView];
        [fullscreenWindow makeKeyAndOrderFront: self];
        [bezelWindow setAlphaValue: 0];
        [fullscreenWindow addChildWindow: bezelWindow ordered: NSWindowAbove];
    }
    else
    {
        SetSystemUIMode(kUIModeNormal, 0);
        [fullscreenWindow orderOut: self];
        [pageScrollView removeFromSuperview];
        [[[self window] contentView] addSubview: pageScrollView];
        contentRect.size = [[pageView window] contentRectForFrameRect: [[self window] frame]].size;
        contentRect.origin.y = [(NFIWindow *)[self window] bottomBarHeight] + 1;
        contentRect.size.height -= contentRect.origin.y;
        [pageScrollView setFrame: contentRect];
        [self resizeWindow];
        [pageView resizeView];
        [[self window] makeKeyAndOrderFront: self];
    }
    
    [[loupeWindow parentWindow] removeChildWindow: loupeWindow];
    [loupeWindow orderOut: self];
    [self refreshLoupePanel];
    [pageScrollView release];
}



- (void)scaleToWindow
{
    [self zoomReset: self];
    BOOL hasVert = NO;
    BOOL hasHor = NO;
    if([[[[NSUserDefaultsController sharedUserDefaultsController] values] valueForKey: TSSTScrollersVisible] boolValue])
    {
        switch ([[session valueForKey: TSSTPageScaleOptions] intValue])
        {
        case  0:
            hasVert = YES;
            hasHor = YES;
            break;
        case  2:
            if([pageView rotation] == 1 || [pageView rotation] == 3)
            {
                hasHor = YES;
            }
            else
            {
                hasVert = YES;
            }
            break;
        default:
            break;
        }
    }
    
    [pageScrollView setHasVerticalScroller: hasVert];
    [pageScrollView setHasHorizontalScroller: hasHor];
    [self resizeWindow];
    [pageView resizeView];
    [self refreshLoupePanel];
}



- (void)animateBezel
{
    if([[session valueForKey: TSSTFullscreen] boolValue])
    {
        [bezelAnimation stopAnimation];
        [bezelWindow setAlphaValue: 0.8];
        NSArray * animations = [NSArray arrayWithObject: [NSDictionary dictionaryWithObjectsAndKeys: bezelWindow, NSViewAnimationTargetKey, NSViewAnimationFadeOutEffect, NSViewAnimationEffectKey,nil]];
        bezelAnimation = [[NSViewAnimation alloc] initWithViewAnimations: animations];
        [bezelAnimation setDelegate: self];
        [bezelAnimation setAnimationCurve: NSAnimationEaseIn];
        [bezelAnimation setDuration: 2.0];
        [bezelAnimation startAnimation];
    }
}


- (void)animationDidStop:(NSAnimation*)animation
{
    if(animation == bezelAnimation)
    {
        [bezelAnimation release];
        bezelAnimation = nil;
    }
}

- (void)animationDidEnd:(NSAnimation*)animation
{
    if(animation == bezelAnimation)
    {
        [bezelAnimation release];
        bezelAnimation = nil; 
    }
}

//- (BOOL)animationShouldStart:(NSAnimation*)animation
//{
//    NSLog(@"start");
//    return YES;
//}



/*! Selects the next non visible page.  Logic looks figures out which 
images are currently visible and then skips over them.
*/
- (void)nextPage
{
    if(![[session valueForKey: TSSTTwoPageSpread] boolValue])
    {
        [pageController selectNext: self];
        return;
    }
    
    int numberOfImages = [[pageController arrangedObjects] count];
	int selectionIndex = [pageController selectionIndex];
	if((selectionIndex + 1) >= numberOfImages)
	{
		return;
	}
	
	BOOL current = [[[pageController arrangedObjects] objectAtIndex: selectionIndex] hasAllowedAspectRatio];
	BOOL next = [[[pageController arrangedObjects] objectAtIndex: (selectionIndex + 1)] hasAllowedAspectRatio];
	if((!current || !next) && ((selectionIndex + 1) < numberOfImages))
	{
		[pageController setSelectionIndex: (selectionIndex + 1)];
	}
	else if((selectionIndex + 2) < numberOfImages)
	{
		[pageController setSelectionIndex: (selectionIndex + 2)];
	}
	else if(((selectionIndex + 1) < numberOfImages) && !next)
	{
		[pageController setSelectionIndex: (selectionIndex + 1)];
	}
}



/*! Selects the previous non visible page.  Logic looks figures out which 
images are currently visible and then skips over them.
*/
- (void)previousPage
{
    if(![[session valueForKey: TSSTTwoPageSpread] boolValue])
    {
        [pageController selectPrevious: self];
        return;
    }
    
	int selectionIndex = [pageController selectionIndex];
	if((selectionIndex - 2) >= 0)
	{
        BOOL previousPage = [[[pageController arrangedObjects] objectAtIndex: (selectionIndex - 1)] hasAllowedAspectRatio];
		BOOL pageBeforeLast = [[[pageController arrangedObjects] objectAtIndex: (selectionIndex - 2)] hasAllowedAspectRatio];
		if(!previousPage || !pageBeforeLast)
		{
			[pageController setSelectionIndex: (selectionIndex - 1)];
			return;
		}
		[pageController setSelectionIndex: (selectionIndex - 2)];
		return;
	}
	
	if((selectionIndex - 1) >= 0)
	{
		[pageController setSelectionIndex: (selectionIndex - 1)];
	}
}



/*! This method is called in preparation for saving. */
- (void)updateSessionObject
{
    if(![[session valueForKey: TSSTFullscreen] boolValue])
    {
        NSValue * postionValue = [NSValue valueWithRect: [[self window] frame]];
        NSData * posData = [NSArchiver archivedDataWithRootObject: postionValue];
        [session setValue: posData forKey: @"position" ];
        
        postionValue = [NSValue valueWithPoint: [[pageView enclosingScrollView] documentVisibleRect].origin];
        posData = [NSArchiver archivedDataWithRootObject: postionValue];
        [session setValue: posData forKey: TSSTScrollPosition ];
    }
    else
    {
        [session setValue: nil forKey: TSSTScrollPosition ];
    }
}


- (void)killOptionalUIElements
{
    loupe = NO;
    [self refreshLoupePanel];
    [exposeBezel orderOut: self];
}



#pragma mark -
#pragma mark Binding Methods



/*!  */
- (NSArray *)pageSortDescriptor
{
    TSSTSortDescriptor * fileNameSort = [[[TSSTSortDescriptor alloc] initWithKey: @"imagePath" ascending: YES] autorelease];
    TSSTSortDescriptor * archivePathSort = [[[TSSTSortDescriptor alloc] initWithKey: @"group.name" ascending: YES] autorelease];
	return [NSArray arrayWithObjects: archivePathSort, fileNameSort, nil];
}



- (NSManagedObject *)session
{
    return session;
}



- (NSManagedObjectContext *)managedObjectContext
{
    return [[NSApp delegate] managedObjectContext];
}



- (NSString *)pageNames
{
    return pageNames;
}



- (void)setPageNames:(NSString *)name
{
    [pageNames release];
    pageNames = [name retain];
    
    if([[session valueForKey: TSSTFullscreen] boolValue])
    {
        [bezelText setStringValue: name];
        NSAttributedString * string = [bezelText attributedStringValue];
        NSSize stringSize = [string size];
        NSRect frame = [fullscreenWindow frame];
        NSRect bezelRect = [bezelWindow frame];
        bezelRect.size.width = stringSize.width + 30;
        bezelRect.origin.x = (NSWidth(frame) - NSWidth(bezelRect)) / 2 + NSMinX(frame);
        bezelRect.origin.y = NSMinY(frame) + 10;
        [bezelWindow setFrame: bezelRect display: YES];
		if([[[[NSUserDefaultsController sharedUserDefaultsController] values] valueForKey: @"fullscreenBezel"] boolValue])
		{
			[self animateBezel];
		}
    }
}



- (void)setPageTurn:(int)turn
{
    pageTurn = turn;
}



- (int)pageTurn
{
    return pageTurn;
}



#pragma mark Menus



- (BOOL)validateMenuItem:(id <NSMenuItem>)menuItem
{
    int state;
    if([menuItem action] == @selector(changeFullscreen:))
    {
        state = [[session valueForKey: TSSTFullscreen] boolValue] ? NSOnState : NSOffState;
        [menuItem setState: state];
    }
    else if([menuItem action] == @selector(changeTwoPage:))
    {
        state = [[session valueForKey: TSSTTwoPageSpread] boolValue] ? NSOnState : NSOffState;
        [menuItem setState: state];
    }
//    else if(([menuItem action] == @selector(zoomIn:) || [menuItem action] == @selector(zoomOut:) || [menuItem action] == @selector(zoomReset:)) &&
//             !([[session valueForKey: TSSTPageScaleOptions] intValue] == 0 || [[session valueForKey: TSSTPageScaleOptions] intValue] == 3))
//    {
//        return NO;
//    }
    else if([menuItem action] == @selector(changePageOrder:))
    {
        if([[session valueForKey: TSSTPageOrder] boolValue])
        {
            [menuItem setTitle: NSLocalizedString(@"Right To Left", @"Right to left page order menu item text")];
        }
        else
        {
            [menuItem setTitle: NSLocalizedString(@"Left To Right", @"Left to right page order menu item text")];
        }
    }
    else if([menuItem tag] == 400)
    {
        state = [[session valueForKey: TSSTPageScaleOptions] intValue] == 0 ? NSOnState : NSOffState;
        [menuItem setState: state];
    }
    else if([menuItem tag] == 401)
    {
        state = [[session valueForKey: TSSTPageScaleOptions] intValue] == 1 ? NSOnState : NSOffState;
        [menuItem setState: state];
    }
    else if([menuItem tag] == 402)
    {
        state = [[session valueForKey: TSSTPageScaleOptions] intValue] == 2 ? NSOnState : NSOffState;
        [menuItem setState: state];
    }
    else if([menuItem tag] == 403)
    {
        state = [[session valueForKey: TSSTPageScaleOptions] intValue] == 3 ? NSOnState : NSOffState;
        [menuItem setState: state];
    }
    return YES;
}



#pragma mark -
#pragma mark Delegates



- (BOOL)windowShouldClose:(id)sender
{
    [NSCursor unhide];
    SetSystemUIMode(kUIModeNormal, 0);
    [[NSApp delegate] endSession: self];
	closing = YES;
    return YES;
}



- (void)windowDidBecomeKey:(NSNotification *)aNotification
{
    if([aNotification object] == fullscreenWindow)
    {
        SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);
        [pageView setNeedsDisplay: YES];
    }
    
    if([aNotification object] == [self window])
    {
        SetSystemUIMode(kUIModeNormal, 0);
    }
}



- (void)windowDidResignKey:(NSNotification *)aNotification
{
    if([aNotification object] == exposeBezel)
    {
        [exposeBezel orderOut: self];
    }
}



- (void)windowDidResize:(NSNotification *)aNotification
{
    NSRect frame;
    if([aNotification object] == [self window])
    {
        [[[self window] contentView] removeTrackingRect: progressRectTrackTag];
        NSRect progressRect = [[[self window]contentView] convertRect: [progressBar progressRect] fromView: progressBar];
        progressRectTrackTag = [[[self window] contentView] addTrackingRect: progressRect owner: self userData: nil assumeInside: NO];
        [pageView resizeView];
    }
    else if([aNotification object] == fullscreenWindow)
    {
        frame = [fullscreenWindow frame];
        NSRect bezelRect = [bezelWindow frame];
        bezelRect.origin.x = NSWidth(frame) / 2 - NSWidth(bezelRect) / 2 + NSMinX(frame);
        bezelRect.origin.y = NSMaxY(frame) - NSHeight(bezelRect) - 10; 
        [bezelWindow setFrame: bezelRect display: YES];
    }
}



- (NSRect)windowWillUseStandardFrame:(NSWindow *)sender defaultFrame:(NSRect)defaultFrame
{
    if(sender == [self window])
    {
        NSSize maxImageSize = [pageView combinedImageSizeForZoomLevel: [pageView zoomLevel]];
        float vertOffset = [(NFIWindow *)[self window] bottomBarHeight] + [(NFIWindow *)[self window] titleBarHeight] + 2;
        if([pageScrollView hasHorizontalScroller])
        {
            vertOffset += NSHeight([[pageScrollView horizontalScroller] frame]);
        }
        float horOffset = [pageScrollView hasVerticalScroller] ? NSWidth([[pageScrollView verticalScroller] frame]) : 0;
        
        NSRect correctedFrame = defaultFrame;
        correctedFrame.size.width -= horOffset;
        correctedFrame.size.height -= vertOffset;
        NSSize newSize;
        if([[session valueForKey: TSSTPageScaleOptions] intValue] == 1)
        {
            float scale;
            if( maxImageSize.width < NSWidth(correctedFrame) && maxImageSize.height < NSHeight(correctedFrame))
            {
                scale = 1;
            }
            else if( NSWidth(correctedFrame) / NSHeight(correctedFrame) < maxImageSize.width / maxImageSize.height)
            {
                scale = NSWidth(correctedFrame) / maxImageSize.width;
            }
            else
            {
                scale = NSHeight(correctedFrame) / maxImageSize.height;
            }
            newSize = scaleSize(maxImageSize, scale);
        }
        else
        {
            newSize.width = maxImageSize.width < NSWidth(correctedFrame) ? maxImageSize.width : NSWidth(correctedFrame);
            newSize.height = maxImageSize.height < NSHeight(correctedFrame) ? maxImageSize.height : NSHeight(correctedFrame);
        }
		NSSize minSize = [[self window] minSize];
		newSize.width = newSize.width < minSize.width ? minSize.width : newSize.width;
		newSize.height = newSize.height < minSize.height ? minSize.height : newSize.height;
        NSRect windowFrame = [[self window] frame];
        NSPoint centerPoint = NSMakePoint(NSMinX(windowFrame) + NSWidth(windowFrame) / 2, 
                                          NSMinY(windowFrame) + NSHeight(windowFrame) / 2);
		newSize.width += horOffset;
        newSize.height += vertOffset;
		NSRect screenRect = [[[self window] screen] visibleFrame];
		
        if((NSMinX(windowFrame) + newSize.width) > NSWidth(screenRect))
		{
			windowFrame.origin.x = NSWidth(screenRect) - newSize.width;
		}
		
		windowFrame.origin.y += NSHeight(windowFrame) - newSize.height;
		if((NSMinY(windowFrame) + newSize.height) > NSHeight(screenRect))
		{
			windowFrame.origin.y = NSHeight(screenRect) - newSize.height;
		}
		
        defaultFrame = NSMakeRect( centerPoint.x - newSize.width / 2, centerPoint.y - newSize.height / 2, newSize.width, newSize.height);
        defaultFrame.origin.x = defaultFrame.origin.x > 0 ? defaultFrame.origin.x : 0;
        defaultFrame.origin.y = defaultFrame.origin.y > 0 ? defaultFrame.origin.y : 0;
    }
    return defaultFrame;
}



@end


